package golangAps

import (
	"errors"
	"fmt"
	"time"
)

func (s *Scheduler) AddJob(j Job) (Job, error) {
	var err error
	ctx := NewContext()
	defer func() {
		EventChan <- EventInfo{
			Ctx:       ctx,
			EventCode: EVENT_JOB_ADDED,
			Job:       &j,
			Error:     err,
		}
	}()

	if j.Id == "" {
		err = JobIdError("is can not empty")
		return j, err
	}
	if err = j.Init(); err != nil {
		return Job{}, err
	}

	DefaultLog.Info(ctx, fmt.Sprintf("Scheduler add job `%s`.", j.Name))

	err = s.SetStore(j.StoreName)
	if err != nil {
		DefaultLog.Info(ctx, "Scheduler add store err:", err)
		return Job{}, err
	}

	job, err := s.store.GetJob(j.StoreName, j.Id)
	if err != nil && !errors.As(err, &JobNotFoundErrorType) {
		return Job{}, err
	}

	if job.StoreName != "" && job.StoreName != j.StoreName {
		// 存在
		if j.Replace {
			// 此处需要注意并发问题， 需要检查这个任务是不是在运行中是的话需要等待不在运行状态
			err := s.store.UpdateJobWithNoRunning(j)
			if err != nil {
				DefaultLog.Error(ctx, "add job error", "job", j, "error", err)
				return j, err
			}
			DefaultLog.Info(ctx, "add job to update", "job", j)
		} else {
			err = JobExistsError(fmt.Sprintf("%s exists %s, can't update", j.Id, j.StoreName))
			return j, err
		}
	} else {
		err := s.store.AddJob(j)
		if err != nil {
			DefaultLog.Error(ctx, "add job error", "job", j, "error", err)
			return j, err
		}
		DefaultLog.Info(ctx, "add job", "job", j)
	}

	if s.isRunning {
		if sj, ok := s.storeJobs.Load(j.StoreName); ok {
			select {
			case sj.(*StoreJob).jobChangeChan <- 3:
			default:
			}
		}
	}
	return j, nil
}

func (s *Scheduler) DeleteJob(id string, bucket string) (err error) {
	err = s.store.DeleteJobWithNoRunning(bucket, []byte(id))
	if err != nil {
		return err
	}
	if sj, ok := s.storeJobs.Load(bucket); ok {
		select {
		case sj.(*StoreJob).jobChangeChan <- 4:
		default:
		}
	}
	return nil
}

func (s *Scheduler) DeleteAllJobs(bucket string) (err error) {
	var storeNames string
	ctx := NewContext()
	defer func() {
		EventChan <- EventInfo{
			Ctx:       ctx,
			EventCode: EVENT_ALL_JOBS_REMOVED,
			Error:     err,
			Msg:       storeNames,
		}
	}()

	DefaultLog.Info(ctx, "delete all jobs.")
	err = s.RemoveStore(bucket)
	if err != nil {
		return err
	}
	return
}

// QueryJob 查询job
func (s *Scheduler) QueryJob(bucket, id string) (Job, error) {
	job, err := s.store.GetJob(bucket, id)
	if err != nil {
		return Job{}, err
	}
	return job, nil
}

// GetJobsByStoreName 获取指定 store 下所有的job
func (s *Scheduler) GetJobsByStoreName(bucket string) ([]Job, error) {
	jobs, err := s.store.GetAllJobs(bucket)
	if err != nil {
		return nil, err
	}
	return jobs, nil
}

// UpdateJob [job.Id, job.StoreName] 不能修改
func (s *Scheduler) UpdateJob(j Job) (Job, error) {
	var err error
	ctx := NewContext()
	defer func() {
		EventChan <- EventInfo{
			Ctx:       ctx,
			EventCode: EVENT_JOB_MODIFIED,
			Job:       &j,
			Error:     err,
		}
	}()

	err = j.Init()
	if err != nil {
		return j, err
	}

	// get old job
	oldJob, err := s.store.GetJob(j.StoreName, j.Id)
	if err != nil {
		return Job{}, err
	}
	if j.Status == "" || (j.Status != STATUS_RUNNING && j.Status != STATUS_PAUSED) {
		j.Status = oldJob.Status
	}

	err = s.store.UpdateJobWithNoRunning(j)
	if err != nil {
		return Job{}, err
	}
	if s.isRunning {
		if sj, ok := s.storeJobs.Load(j.StoreName); ok {
			select {
			case sj.(*StoreJob).jobChangeChan <- 5:
			default:
			}
		}
	}
	return j, nil
}

func (s *Scheduler) PauseJob(bucket, id string) (Job, error) {

	ctx := NewContext()
	DefaultLog.Info(ctx, "pause job", "jobId", id)
	// get old job
	job, err := s.store.GetJob(bucket, id)
	if err != nil {
		return Job{}, err
	}

	job.Status = STATUS_PAUSED
	now := time.Now().Add(time.Hour * 24 * 365 * 100).UTC().Unix()
	job.NextRunTime = now

	err = s.store.UpdateJobWithNoRunning(job)
	if err != nil {
		return Job{}, err
	}
	if s.isRunning {
		if sj, ok := s.storeJobs.Load(job.StoreName); ok {
			select {
			case sj.(*StoreJob).jobChangeChan <- 6:
			default:
			}
		}
	}
	return job, nil
}

func (s *Scheduler) ResumeJob(bucket, id string) (Job, error) {
	ctx := NewContext()
	DefaultLog.Info(ctx, "Scheduler resume job", "jobId", id)

	job, err := s.store.GetJob(bucket, id)
	if err != nil {
		return Job{}, err
	}

	job.Status = STATUS_RUNNING
	now := time.Now().UTC().Unix()
	job.NextRunTime, _ = job.Trigger.GetNextRunTime(0, now)

	err = s.store.UpdateJobWithNoRunning(job)
	if err != nil {
		return Job{}, err
	}
	if s.isRunning {
		if sj, ok := s.storeJobs.Load(job.StoreName); ok {
			select {
			case sj.(*StoreJob).jobChangeChan <- 7:
			default:
			}
		}
	}
	return job, nil
}
